package net.softwarefabrik.bde.core.model.platform.internal;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.osgi.framework.Constants;

import net.softwarefabrik.bde.core.model.BDEConstants;
import net.softwarefabrik.bde.core.model.BDEException;
import net.softwarefabrik.bde.core.model.maven.RepositoryManager;
import net.softwarefabrik.bde.core.model.platform.BundleInfo;
import net.softwarefabrik.bde.core.model.platform.PlatformConfiguration;
import net.softwarefabrik.bde.core.model.platform.internal.AbstractPlatformProvider;
import net.softwarefabrik.bde.core.util.FileUtil;

/**
 * A <code>PlatformProvider</code> for the Felix OSGi platform.
 * 
 * @see http://felix.apache.org
 * @author Matthias Kuespert
 */
public class FelixPlatformProvider extends AbstractPlatformProvider {

    public FelixPlatformProvider(RepositoryManager repositoryManager) {
        super(repositoryManager);
    }
    
    /**
     * @see net.softwarefabrik.bde.core.model.platform.internal.AbstractPlatformProvider#getArtifactId()
     */
    public String getArtifactId() {
        return "org.apache.felix.main";
    }
    
    /**
     * @see net.softwarefabrik.bde.core.model.platform.internal.AbstractPlatformProvider#getGroupId()
     */
    public String getGroupId() {
        return "org.apache.felix";
    }
    
    /**
     * @return The name of the platform.
     * 
     * @see net.softwarefabrik.bde.core.model.platform.PlatformProvider#getPlatformName()
     */
    public String getPlatformName() {
        return "Felix";
    }

    /**
     * @param config The configuration to use.
     * @return The name of the main class to use for starting the platform.
     * 
     * @see net.softwarefabrik.bde.core.model.platform.PlatformProvider#getMainClass(net.softwarefabrik.bde.core.model.platform.PlatformConfiguration)
     */
    public String getMainClass() {
        return "org.apache.felix.main.Main";
    }

    /**
     * @see net.softwarefabrik.bde.core.model.platform.internal.AbstractPlatformProvider#getCacheDir()
     */
    @Override
    protected String getCacheDir() {
        return getPlatformId() + BDEConstants.STD_FILE_SEPARATOR + "cache";
    }

    /**
     * @see net.softwarefabrik.bde.core.model.platform.internal.AbstractPlatformProvider#getConfigFile()
     */
    @Override
    protected String getConfigFile() {
        return getPlatformId() + BDEConstants.STD_FILE_SEPARATOR + "config.ini";
    }

    /**
     * @param config The configuration to use.
     * @return A <code>String</code> array with the runtime arguments.
     * @throws BDEException if something goes wrong
     * 
     * @see net.softwarefabrik.bde.core.model.platform.PlatformProvider#getRuntimeArguments(net.softwarefabrik.bde.core.model.platform.PlatformConfiguration)
     */
    public String[] getRuntimeArguments(PlatformConfiguration config) throws BDEException {
        return new String[] {};
    }

    /**
     * @param config The configuration to use.
     * @return A <code>String</code> array with the VM options.
     * @throws BDEException if something goes wrong
     * 
     * @see net.softwarefabrik.bde.core.model.platform.PlatformProvider#getRuntimeVMOptions(net.softwarefabrik.bde.core.model.platform.PlatformConfiguration)
     */
    public String[] getRuntimeVMOptions(PlatformConfiguration config) throws BDEException {
        if (null == config) {
            throw new BDEException("No PlatformConfiguration object specified.");
        }
        String workingDirectory = config.getWorkingDirectory();
        if (null == workingDirectory || BDEConstants.EMPTY_STRING.equals(workingDirectory)) {
            throw new BDEException("No working directory specified for platform "
                                   + getPlatformName());
        }
        return new String[] { "-Dfelix.config.properties=file:"
                              + workingDirectory
                              + BDEConstants.STD_FILE_SEPARATOR
                              + getConfigFile()
                            };
    }

    /**
     * @param config The configuration to use.
     * @throws BDEException if something goes wrong
     * 
     * @see net.softwarefabrik.bde.core.model.platform.internal.AbstractPlatformProvider#createRuntimeConfiguration(net.softwarefabrik.bde.core.model.platform.PlatformConfiguration)
     */
    protected void createRuntimeConfiguration(PlatformConfiguration config) throws BDEException {
        StringBuffer content = new StringBuffer();
        content
               .append("# ----------------------------------------------------------------------------\n");
        content.append("# Felix configuration created by bde.platform.felix plugin\n");
        content.append("# THIS IS A GENERATED FILE - DO NOT EDIT!\n");
        content
               .append("# ----------------------------------------------------------------------------\n");
        content.append("# Framework settings:\n");
        content.append("#\n");
        content.append("felix.startlevel.framework=" + config.getPlatformStartLevel() + "\n");
        content.append("felix.startlevel.bundle=" + config.getDefaultBundleStartLevel() + "\n");
        content.append("felix.cache.profile=" + "cache" + "\n");
        content.append("felix.cache.profiledir="
                       + config.getWorkingDirectory()
                       + "/"
                       + getCacheDir()
                       + "\n");
        content.append(Constants.FRAMEWORK_SYSTEMPACKAGES
                       + "="
                       + "org.osgi.framework; version=1.3.0, \\\n"
                       + "org.osgi.service.packageadmin; version=1.2.0, \\\n"
                       + "org.osgi.service.startlevel; version=1.0.0, \\\n"
                       + "org.osgi.service.url; version=1.0.0, \\\n"
                       + "org.osgi.util.tracker; version=1.3.2\n");
        // + "${jre-${java.specification.version}}\n");

        content.append("#\n");
        content
               .append("# ----------------------------------------------------------------------------\n");
        content.append("# Bundle configuration\n");
        content.append("#\n");
        // collect bundles sorted by startlevel
        Map<Integer, Collection<BundleInfo>> bundles = new HashMap<Integer, Collection<BundleInfo>>();
        for (BundleInfo bi : config.getBundles()) {
            if (!bundles.containsKey(bi.getStartLevel())) {
                bundles.put(new Integer(bi.getStartLevel()), new ArrayList<BundleInfo>());
            }
            bundles.get(bi.getStartLevel()).add(bi);
        }
        // and add to autoInstall or autoStart as appropriate
        for (Integer startLevel : bundles.keySet()) {
            StringBuffer autoInstall = new StringBuffer();
            StringBuffer autoStart = new StringBuffer();
            boolean first = true;
            for (BundleInfo bi : bundles.get(startLevel)) {
                String entry = "file:" + bi.getPath();
                if (first) {
                    first = false;
                } else {
                    entry = " \\\n " + entry;
                }
                //
                if (bi.doStart()) {
                    autoStart.append(entry);
                } else {
                    autoInstall.append(entry);
                }
            }
            //
            if (autoInstall.length() > 0) {
                content.append("felix.auto.install." + startLevel.toString() + " = ")
                       .append(autoInstall).append("\n");
            }
            if (autoStart.length() > 0) {
                content.append("felix.auto.start." + startLevel.toString() + " = ")
                       .append(autoStart).append("\n");
            }
        }
        String configFileName = config.getWorkingDirectory()
                                + BDEConstants.STD_FILE_SEPARATOR
                                + getConfigFile();
        FileUtil.writeFile(configFileName, content.toString());
    }
}
